home *** CD-ROM | disk | FTP | other *** search
- /*
-
- File: dir_fat.c
-
- Copyright (C) 1998-2004 Christophe GRENIER <grenier@cgsecurity.org>
-
- This software is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-
- #include <stdio.h>
- #include <ctype.h> /* toupper */
- #include <string.h>
- #include <stdlib.h>
- #include <time.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include "types.h"
- #include "common.h"
- #include "fat.h"
- #include "lang.h"
- #include "fnctdsk.h"
- #include "testdisk.h"
- #include "intrface.h"
- #include "dir.h"
- #include "fat_dir.h"
-
- static inline void fat16_towchar(wchar_t *dst, const __u8 *src, size_t len);
-
- static inline void fat16_towchar(wchar_t *dst, const __u8 *src, size_t len)
- {
- while (len--) {
- *dst++ = src[0] | (src[1] << 8);
- src += 2;
- }
- }
-
- t_file_data *dir_fat_aux(const char*buffer, const unsigned int size, const unsigned int cluster_size)
- {
- const struct msdos_dir_entry *de=(const struct msdos_dir_entry*)buffer;
- wchar_t unicode[1000];
- unsigned char long_slots;
- t_file_data *dir_list=NULL;
- t_file_data *current_file=NULL;
- GetNew:
- long_slots = 0;
- unicode[0]=0;
- if (de->name[0] == (__s8) DELETED_FLAG)
- goto RecEnd;
- if (de->attr == ATTR_EXT) {
- unsigned int i;
- const struct msdos_dir_slot *ds;
- unsigned char id;
- unsigned char slot;
- unsigned char slots;
- unsigned char sum;
- unsigned char alias_checksum;
- ParseLong:
- slots = 0;
- ds = (const struct msdos_dir_slot *) de;
- id = ds->id;
- if ((id & 0x40)==0)
- goto RecEnd;
- slots = id & ~0x40;
- if (slots > 20 || slots==0) /* ceil(256 * 2 / 26) */
- goto RecEnd;
- long_slots = slots;
- alias_checksum = ds->alias_checksum;
-
- slot = slots;
- while (1) {
- int offset;
-
- slot--;
- offset = slot * 13;
- fat16_towchar(unicode + offset, ds->name0_4, 5);
- fat16_towchar(unicode + offset + 5, ds->name5_10, 6);
- fat16_towchar(unicode + offset + 11, ds->name11_12, 2);
-
- if ((ds->id & 0x40)!=0) {
- unicode[offset + 13] = 0;
- }
- de++;
- if((const void*)de>=(const void*)(buffer+size))
- goto EODir;
- if (slot == 0)
- break;
- ds = (const struct msdos_dir_slot *) de;
- if (ds->attr != ATTR_EXT)
- goto RecEnd; /* XXX */
- if ((ds->id & ~0x40) != slot)
- goto ParseLong;
- if (ds->alias_checksum != alias_checksum)
- goto ParseLong;
- }
- if (de->name[0] == (__s8) DELETED_FLAG)
- goto RecEnd;
- if (de->attr == ATTR_EXT)
- goto ParseLong;
- if (IS_FREE(de->name) || ((de->attr & ATTR_VOLUME)!=0))
- goto RecEnd;
- for (sum = 0, i = 0; i < 11; i++)
- sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
- if (sum != alias_checksum)
- long_slots = 0;
- }
- RecEnd:
- if((unicode[0]==0) &&(de->attr != ATTR_EXT))
- { /* short name 8.3 */
- int i;
- int j=0;
- for(i=0;(i<8)&&(de->name[i]!=' ');i++)
- unicode[j++]=de->name[i];
- if(de->ext[0]!=' ')
- {
- unicode[j++]='.';
- for(i=0;(i<3)&&(de->ext[i]!=' ');i++)
- unicode[j++]=de->ext[i];
- }
- unicode[j]=0;
- }
- if (((de->attr != ATTR_EXT)||(long_slots!=0)) && ((__s8) unicode[0] != (__s8) DELETED_FLAG) && !(de->attr & ATTR_VOLUME))
- {
- if(unicode[0]!=0)
- {
- unsigned int i;
- t_file_data *new_file=MALLOC(sizeof(*new_file));
- for(i=0;(unicode[i]!=0)&&(i<sizeof(new_file->name)-1);i++)
- new_file->name[i]=(char) unicode[i];
- new_file->name[i]=0;
- new_file->filestat.st_dev=0;
- new_file->filestat.st_ino=(de->starthi<<16)|de->start;
- new_file->filestat.st_mode = MSDOS_MKMODE(de->attr,(LINUX_S_IRWXUGO & ~(LINUX_S_IWGRP|LINUX_S_IWOTH)));
- new_file->filestat.st_nlink=0;
- new_file->filestat.st_uid=0;
- new_file->filestat.st_gid=0;
- new_file->filestat.st_rdev=0;
- new_file->filestat.st_size=de->size;
- new_file->filestat.st_blksize=cluster_size;
- #ifndef DJGPP
- if(new_file->filestat.st_blksize!=0)
- {
- new_file->filestat.st_blocks=(new_file->filestat.st_size+new_file->filestat.st_blksize-1)/new_file->filestat.st_blksize;
- }
- #endif
- new_file->filestat.st_atime=new_file->filestat.st_ctime=new_file->filestat.st_mtime=date_dos2unix(de->time,de->date);
- new_file->prev=current_file;
- new_file->next=NULL;
- /* ecrit_rapport("fat: new file %s de=%p size=%u\n",new_file->name,de,de->size); */
- /* fat_write_rapport(new_fat_element); */
- if(current_file!=NULL)
- current_file->next=new_file;
- else
- dir_list=new_file;
- current_file=new_file;
- }
- else
- {
- return dir_list;
- }
- }
- de++;
- if((const void *)de<(const void *)(buffer+size))
- goto GetNew;
- EODir:
- return dir_list;
- }
-
- static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
- /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
-
- /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
-
- int date_dos2unix(const unsigned short f_time, const unsigned short f_date)
- {
- int month,year,secs;
-
- /* first subtract and mask after that... Otherwise, if
- f_date == 0, bad things happen */
- month = ((f_date >> 5) - 1) & 15;
- year = f_date >> 9;
- secs = (f_time & 31)*2+60*((f_time >> 5) & 63)+(f_time >> 11)*3600+86400*
- ((f_date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
- month < 2 ? 1 : 0)+3653);
- /* days since 1.1.70 plus 80's leap day */
- return secs;
- }
-
- t_file_data *fat12_dir(t_param_disk *disk_car, const t_diskext *partition, t_dir_data *dir_data, const unsigned long int first_cluster)
- {
- struct fat_boot_sector*fat_header=(struct fat_boot_sector*)dir_data->private_dir_data;
- if(fat_header->cluster_size<1)
- {
- ecrit_rapport(msg_ROOT_CLUSTER_RERR);
- return NULL;
- }
- if(first_cluster==0)
- return fat1x_rootdir(disk_car,partition,dir_data->debug,fat_header);
- {
- t_file_data *dir_list;
- unsigned int cluster_size=fat_header->cluster_size;
- unsigned char *buffer_dir=MALLOC(SECTOR_SIZE*cluster_size*5);
- unsigned int cluster;
- unsigned int nbr_cluster;
- memset(buffer_dir,0,SECTOR_SIZE*cluster_size*5);
- for(cluster=first_cluster, nbr_cluster=0;
- ((cluster&0x0ff8)!=(unsigned)0x0ff8) && (cluster>=2) && (nbr_cluster<5);
- cluster=get_next_cluster(disk_car,partition, UP_FAT12,le16(fat_header->reserved), cluster), nbr_cluster++)
- {
- dword start=partition->lba+le16(fat_header->reserved)+fat_header->fats*le16(fat_header->fat_length)+(get_dir_entries(fat_header)*32+SECTOR_SIZE-1)/SECTOR_SIZE+(cluster-2)*cluster_size;
- if(dir_data->debug)
- ecrit_rapport("FAT12 cluster=%u(0x%x), pos=%lu\n",cluster,cluster,start);
- if(disk_car->read(disk_car, cluster_size, buffer_dir+SECTOR_SIZE*cluster_size*nbr_cluster, start))
- {
- ecrit_rapport(msg_ROOT_CLUSTER_RERR);
- FREE(buffer_dir);
- return NULL;
- }
- }
- dir_list=dir_fat_aux(buffer_dir,SECTOR_SIZE*cluster_size*nbr_cluster,cluster_size);
- FREE(buffer_dir);
- return dir_list;
- }
- }
-
- t_file_data *fat16_dir(t_param_disk *disk_car, const t_diskext *partition, t_dir_data *dir_data, const unsigned long int first_cluster)
- {
- struct fat_boot_sector*fat_header=(struct fat_boot_sector*)dir_data->private_dir_data;
- if(fat_header->cluster_size<1)
- {
- ecrit_rapport(msg_ROOT_CLUSTER_RERR);
- return NULL;
- }
- if(first_cluster==0)
- return fat1x_rootdir(disk_car,partition,dir_data->debug,fat_header);
- {
- t_file_data *dir_list=NULL;
- unsigned int cluster_size=fat_header->cluster_size;
- unsigned char *buffer_dir=MALLOC(SECTOR_SIZE*cluster_size*5);
- unsigned int cluster;
- unsigned int nbr_cluster;
- memset(buffer_dir,0,SECTOR_SIZE*cluster_size*5);
- /* Need to correct the test */
- for(cluster=first_cluster, nbr_cluster=0;
- ((cluster&0xfff8)!=(unsigned)0xfff8) && (cluster>=2) && (nbr_cluster<5);
- cluster=get_next_cluster(disk_car,partition, UP_FAT16,le16(fat_header->reserved), cluster), nbr_cluster++)
- {
- dword start=partition->lba+le16(fat_header->reserved)+fat_header->fats*le16(fat_header->fat_length)+(get_dir_entries(fat_header)*32+SECTOR_SIZE-1)/SECTOR_SIZE+(cluster-2)*cluster_size;
- if(dir_data->debug)
- ecrit_rapport("FAT16 cluster=%u(0x%x), pos=%lu\n",cluster,cluster,start);
- if(disk_car->read(disk_car, cluster_size, buffer_dir+SECTOR_SIZE*cluster_size*nbr_cluster, start))
- {
- ecrit_rapport(msg_ROOT_CLUSTER_RERR);
- FREE(buffer_dir);
- return NULL;
- }
- }
- dir_list=dir_fat_aux(buffer_dir,SECTOR_SIZE*cluster_size*nbr_cluster,cluster_size);
- FREE(buffer_dir);
- return dir_list;
- }
- }
-
- t_file_data *fat32_dir(t_param_disk *disk_car, const t_diskext *partition, t_dir_data *dir_data, const unsigned long int first_cluster)
- {
- struct fat_boot_sector*fat_header=(struct fat_boot_sector*)dir_data->private_dir_data;
- if(fat_header->cluster_size==0 || le32(fat_header->root_cluster)==0)
- return NULL;
- {
- t_file_data *dir_list;
- unsigned int cluster_size=fat_header->cluster_size;
- unsigned char *buffer_dir=MALLOC(SECTOR_SIZE*cluster_size*5);
- unsigned int cluster;
- int nbr_cluster;
- memset(buffer_dir,0,SECTOR_SIZE*cluster_size*5);
- /* Need to correct the test */
- for(cluster=(first_cluster==0?le32(fat_header->root_cluster):first_cluster), nbr_cluster=0;
- ((cluster&0xffffff8)!=(unsigned)0xffffff8) && (cluster>=2) && (nbr_cluster<5);
- cluster=get_next_cluster(disk_car,partition, UP_FAT32,le16(fat_header->reserved), cluster), nbr_cluster++)
- {
- dword start=partition->lba+le16(fat_header->reserved)+fat_header->fats*le32(fat_header->fat32_length)+(cluster-2)*cluster_size;
- if(dir_data->debug)
- ecrit_rapport("FAT32 cluster=%u(0x%x), pos=%lu\n",cluster,cluster,start);
- if(disk_car->read(disk_car, cluster_size, buffer_dir+SECTOR_SIZE*cluster_size*nbr_cluster, start))
- {
- ecrit_rapport(msg_ROOT_CLUSTER_RERR);
- FREE(buffer_dir);
- return 0;
- }
- }
- dir_list=dir_fat_aux(buffer_dir,SECTOR_SIZE*cluster_size*5,cluster_size);
- FREE(buffer_dir);
- return dir_list;
- }
- }
-
-
- t_file_data *fat1x_rootdir(t_param_disk *disk_car, const t_diskext *partition, const int debug, struct fat_boot_sector*fat_header)
- {
- unsigned int root_size=(get_dir_entries(fat_header)-1)/16+1;
- dword start=0;
- if(debug>1)
- {
- ecrit_rapport("fat1x_rootdir root_size=%u\n",root_size);
- }
- {
- unsigned char *buffer_dir=(unsigned char*)MALLOC(SECTOR_SIZE*root_size);
- start=partition->lba+le16(fat_header->reserved)+fat_header->fats*le16(fat_header->fat_length);
- if(disk_car->read(disk_car, root_size, buffer_dir, start))
- {
- ecrit_rapport("FAT 1x");
- ecrit_rapport(msg_ROOT_CLUSTER_RERR);
- return NULL;
- }
- return dir_fat_aux(buffer_dir,SECTOR_SIZE*root_size,fat_header->cluster_size);
- }
- }
-
- int dir_partition_fat_aux(WINDOW *window,t_param_disk *disk_car, t_diskext *partition, const int debug, struct fat_boot_sector*fat_header)
- {
- t_dir_data dir_data;
- dir_data.window=window;
- dir_data.debug=debug;
- dir_data.private_dir_data=fat_header;
- switch(partition->upart_type)
- {
- case UP_FAT12:
- dir_data.get_dir=fat12_dir;
- break;
- case UP_FAT16:
- dir_data.get_dir=fat16_dir;
- break;
- case UP_FAT32:
- dir_data.get_dir=fat32_dir;
- break;
- default:
- return -1;
- }
- strncpy(dir_data.current_directory,"/",sizeof(dir_data.current_directory));
- return dir_partition(disk_car,partition,&dir_data,0);
- }
-
- int dir_partition_fat(WINDOW *window,t_param_disk *disk_car, t_diskext *partition, const int debug)
- {
- t_sector buffer;
- if(disk_car->read(disk_car,1, &buffer, partition->lba+partition->boot_sector))
- {
- ecrit_rapport(msg_CHKFAT_RERR); return 1;
- }
- dir_partition_fat_aux(window,disk_car, partition, debug, (struct fat_boot_sector*)buffer);
- return 0;
- }
-